﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Threading;
using UnityEditor;
using UnityEngine;
using UnityEngine.Tilemaps;

[Serializable]
public class NeuralNetShaderFS
{
    [NonSerialized]
    public int count = 0;
    public string name = "iRobot";
    public int saveTime = 5;
    public bool saving = false;
    public int inputNum = 256, inputHeight = 256;
    public int outputNum = 256, outputHeight = 256;
    public int hideLayNum = 32;
    public int hideWidth = 256, hideHeight = 256;
    public int hideLinLen = 24;
    public int hideLinNum = 3;
    public int threadNum = 5;
    public float inputDecRate = 0f, powerIncRate = 0.03f, powerDecRate = 0.003f;
    public float layerIncRate = 0.01f, layerDecRate = 0.01f;
    public bool seatchLink = false;
    public bool initing = false;

    [NonSerialized]
    public bool runing;

    public delegate void EventHandlerUpdate();

    [NonSerialized]
    public EventHandlerUpdate run;
    public int sleep = 8, updateSpeed = 125;//8=120帧/秒；  16=60帧每秒
    private System.Random random = new System.Random();

    private static string persistentDataPath = ".";

    public static int loadData = 0;

    public class NeuralNode
    {
        public int x, y, z;
        public float layer;
        public float input;
        public NeuralNodeLink[] links;
    }

    public struct NeuralNodeLink
    {
        public float power;
        public short linkX;
        public short linkY;
        public short linkZ;
    }

    public struct NeuralLink
    {
        public float input;
        public float layer;
        public float power;
    }

    public struct NeuralData
    {
        public int x;
        public int y;
        public int z;
        public float input;
        public float layer;
        public NeuralLink link1;
        public NeuralLink link2;
        public NeuralLink link3;
    }

    /// <summary>
    /// 缓存表
    /// </summary>
    private List<List<Hashtable>> tables = new List<List<Hashtable>>();

    public NeuralNode GetNode(int x, int y, int z)
    {
        if (tables[x][y].ContainsKey(z))
        {
            return (NeuralNode)tables[x][y][z];
        }
        else
        {
            NeuralNode node = new NeuralNode();
            node.x = x;
            node.y = y;
            node.z = z;
            node.links = new NeuralNodeLink[hideLinNum];
            lock (tables[x][y])
            {
                if (tables[x][y].ContainsKey(z))
                {
                    return (NeuralNode)tables[x][y][z];
                }
                //Debug.Log("NewNode " + x + "," + y + "," + z);
                
                bool suc = BinaryDeserialize(node);
                if (suc == false)
                {
                    for (int k = 0; k < node.links.Length; k++)
                    {
                        node.links[k].linkX = -1;
                        node.links[k].linkY = -1;
                        node.links[k].linkZ = -1;
                    }
                }
                tables[x][y][z] = node;
            }
            return node;
        }
    }

    public bool SaveNode(NeuralNode node)
    {
        if (node == null)
            return false;

        lock (tables[node.x][node.y])
        {
            tables[node.x][node.y][node.z] = node;

            if (BinarySerialize(node))
            {
                //tables[node.x][node.y].Remove(node.z);
                return true;
            }
            else
            {
                return false;
            }
        }
    }

    public void Init() {
        initing = true;
        for (int i = 0; i < hideLayNum + 2; i++)
        {
            List<Hashtable> list = new List<Hashtable>();
            for (int j = 0; j < hideWidth; j++)
            {
                list.Add(new Hashtable());
            }
            tables.Add(list);
        }
        //Debug.Log(name+"  Init InputLay");
        // input layer
        //for (int i = 0; i < inputNum; i++) {
        //    for (int j = 0; j < inputHeight; j++)
        //    {
        //        NeuralNode node = GetNode(0, i, j);
        //        for (int k = 0; k < hideLinNum; k++)
        //        {
        //            int si = (int)Math.Round(1f * hideWidth * i / inputNum);
        //            int sj = (int)Math.Round(1f * hideHeight * j / inputHeight);
                    
        //            node.links[k].linkX = 1;
        //            node.links[k].linkY = (short)random.Next(Math.Max(0, si - hideLinLen), Math.Min(si + hideLinLen + 1, hideWidth));
        //            node.links[k].linkZ = (short)random.Next(Math.Max(0, sj - hideLinLen), Math.Min(sj + hideLinLen + 1, hideHeight));
        //            node.links[k].power = random.Next(-9999, 10000) / 10000f;
        //        }
        //        //SaveNode(node);
        //    }
        //}
        //Debug.Log(name+"  Init HideLay");
        //// hide layer
        //hideLinNum = Mathf.Min(8, hideLinNum);
        //for (int i = 1; i <= hideLayNum; i++) {
        //    count = i;
        //    for (int j = 0; j < hideWidth; j++) {
        //        for (int z = 0; z < hideHeight; z++)
        //        {
        //            NeuralNode node = GetNode(i, j, z);
        //            for (int k = 0; k < hideLinNum; k++)
        //            {
        //                node.links[k].power = random.Next(-9999, 10000) / 10000f; ;
        //                //linkX[i][j][k] = i < hideLayNum ? UnityEngine.Random.Range(1, hideLayNum + 1) : hideLayNum + 1;
        //                //linkY[i][j][k] = UnityEngine.Random.Range(-1, (i < hideLayNum?hideWidth:outputNum));
        //                if (i == hideLayNum)
        //                {
        //                    int sj = (int)Math.Round(1f * j / hideWidth * outputNum);
        //                    int sz = (int)Math.Round(1f * z / hideHeight * outputHeight);
        //                    node.links[k].linkX = (short)(hideLayNum + 1);
        //                    node.links[k].linkY = (short)random.Next(Math.Max(0, sj - hideLinLen), Math.Min(sj + hideLinLen + 1, outputNum));
        //                    node.links[k].linkZ = (short)random.Next(Math.Max(0, sz - hideLinLen), Math.Min(sz + hideLinLen + 1, outputHeight));
        //                    //Debug.Log("HideLayEndLayLink:" +j+"@"+ k + "  ->  " + linkY[i][j][k]);
        //                }
        //                else
        //                {
        //                    node.links[k].linkX = (short)random.Next(Mathf.Max(1, i - hideLinLen), Mathf.Min(i + hideLinLen + 1, hideLayNum + 1));
        //                    node.links[k].linkY = (short)random.Next(Mathf.Max(0, j - hideLinLen), Mathf.Min(j + hideLinLen + 1, hideWidth));
        //                    node.links[k].linkZ = (short)random.Next(Math.Max(0, z - hideLinLen), Math.Min(z + hideLinLen + 1, hideHeight));
        //                }

        //            }
        //            //SaveNode(node);
        //        }
        //    }
        //}

        //// output layer
        //Debug.Log(name+"  Init OutputLay");
        //for (int j = 0; j < outputNum; j++)
        //{
        //    for (int z = 0; z < outputHeight; z++)
        //    {
        //        NeuralNode node = GetNode(hideLinNum + 1, j, z);
        //        node.input = 0;
        //        //SaveNode(node);
        //    }
        //}

        //Debug.Log("NN Init End");
        initing = false;
    }

    public void InitInput()
    {
        if (tables.Count == 0)
        {
            for (int i = 0; i < hideLayNum + 2; i++)
            {
                List<Hashtable> list = new List<Hashtable>();
                for (int j = 0; j < hideWidth; j++)
                {
                    list.Add(new Hashtable());
                }
                tables.Add(list);
            }
        }

        //input = new short[hideLayNum + 2][][];
        //input[0] = new short[inputNum][];
        //for (int i = 0; i < inputNum; i++)
        //{
        //    input[0][i] = new short[inputHeight];
        //}
        //for (int i = 1; i <= hideLayNum; i++)
        //{
        //    input[i] = new short[hideWidth][];
        //    for (int j = 0; j < hideWidth; j++)
        //    {
        //        input[i][j] = new short[hideHeight];
        //    }
        //}
        //input[hideLayNum + 1] = new short[outputNum][];
        //for (int i = 0; i < outputNum; i++)
        //{
        //    input[hideLayNum + 1][i] = new short[outputHeight];
        //}
    }

    //public int inwidth() {
    //    return layer[0].Length;
    //}

    //public int outwidth() {
    //    return layer[hideLayNum + 1].Length;
    //}

    public void inputValue(int j, int z, float val)
    {
        if (j >= 0 && j < inputNum && z >= 0 && z < inputHeight)
        {
            NeuralNode node = GetNode(0, j, z);
            node.input += val;
        }
        //if (short.IsInfinity(input[0][i]))
        //{
        //    input[0][i] = short.MaxValue;
        //}
    }
    public void inputValue(int i, int j, int z, float val)
    {
        GetNode(i, j, z).input += val;
        //if (float.IsInfinity(input[i][j]))
        //{
        //    input[i][j] = float.MaxValue;
        //}
    }
    public short getOutInputLayer(int j, int z)
    {
        NeuralNode node = GetNode(hideLayNum + 1, j, z);
        return (short)(node.input + node.layer);
    }
    public short getInputLayer(int i, int j, int z)
    {
        NeuralNode node = GetNode(i, j, z);
        return (short)(node.input + node.layer);
    }

    public bool checkOut(int j, int z) {

        if (j >= 0 && j < outputNum && z >= 0 && z < outputHeight)
        {
            NeuralNode node = GetNode(hideLayNum + 1, j, z);
            return node.input + node.layer > 0;
        }
        return false;
    }

    public bool check(int i, int j, int z)
    {
        if (i >= 0 && i <= hideLayNum + 1 && j >= 0 && j < hideWidth && z >= 0 && z < hideHeight)
        {
            NeuralNode node = GetNode(i, j, z);
            return node.input + node.layer > 0;
        }
        return false;
    }

    //public void clear(int i)
    //{
    //    if (input != null)
    //    {
    //        input[hideLayNum + 1][i] = 0;
    //    }
    //}

    // Use this for initialization
    public virtual void Start() {
        runing = true;

        for (short i = 0; i < threadNum; i++)
        {
            short tid = i;
            Debug.Log("ThreadUpdateStart:" + tid);

            Thread t = new Thread(() =>
            {
                Thread.Sleep(10000);
                for (long n = 0; runing; n++)
                {
                    ThreadUpdate(tid, n);
                    Thread.Sleep(sleep);
                }
                Debug.Log("ThreadUpdateEnd:" + tid);
            });
            t.IsBackground = true;
            t.Start();  //其实用的也是ThreadStart
        }
        //Thread rthread = new Thread(() =>
        //{
        //    Debug.Log("RootThreadUpdateStart");
        //    while (runing)
        //    {
        //        ThreadUpdate();
        //        Thread.Sleep(1);
        //    }
        //    Debug.Log("RootThreadUpdateEnd");
        //});
        //rthread.Start();  //其实用的也是ThreadStart
        neuralData = new List<NeuralData[]>();
        for (int i = 0; i < threadNum; i++)
        {
            neuralData.Add(new NeuralData[hideWidth * hideHeight]);
        }
    }

    private List<NeuralData[]> neuralData;
    private static object lockObj = new System.Object();

    void ThreadUpdate(short tid, long count)
    {
        short n = (short)(hideLayNum / threadNum), v = 0, tv = 0, k = 0, destX = 0, destY = 0, destZ = 0;
        int tvn = 0,currIdx=0;
        bool hascomp = false;

        if (tid == 0)
        {
            // input layer
            for (int j = 0; j < inputNum; j++)
            {
                for (int z = 0; z < inputHeight; z++)
                {
                    NeuralNode node = GetNode(0, j, z);
                    for (k = 0; k < hideLinNum; k++)
                    {
                        destY = node.links[k].linkY;
                        destZ = node.links[k].linkZ;
                        if (destY < 0 || destZ < 0)
                        {
                            //搜索可连接节点
                            int si = (int)Math.Round(1f * hideWidth * j / inputNum);
                            int sj = (int)Math.Round(1f * hideHeight * z / inputHeight);
                            if (seatchLink)
                            {
                                for (int w = Math.Max(0, si - hideLinLen); destY == -1 && w < Math.Min(si + hideLinLen + 1, hideWidth); w++)
                                {
                                    for (int h = Math.Max(0, sj - hideLinLen); h < Math.Min(sj + hideLinLen + 1, hideHeight); h++)
                                    {
                                        if (getInputLayer(1, w, h) > 0)
                                        {
                                            node.links[k].linkX = 1;
                                            node.links[k].linkY = destY = (short)w;
                                            node.links[k].linkZ = destZ = (short)h;
                                            node.links[k].power = random.Next(-9999, 10000) / 10000f;// * (layer[0][i][j] > layer[1][offsetY][offsetZ] ? 1f : -1f);
                                            break;
                                        }
                                    }
                                    if (destY > 0)
                                    {
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                node.links[k].linkY = destY = (short)random.Next(Math.Max(0, si - hideLinLen), Math.Min(si + hideLinLen + 1, hideWidth));
                                node.links[k].linkZ = destZ = (short)random.Next(Math.Max(0, sj - hideLinLen), Math.Min(sj + hideLinLen + 1, hideHeight));
                                if (getInputLayer(1, destY, destZ) > 0)
                                {
                                    node.links[k].power = random.Next(-9999, 10000) / 10000f;// * (layer[0][i][j] > layer[1][offsetY][offsetZ] ? 1f : -1f);
                                }
                                else
                                {
                                    node.links[k].linkY = destY = -1;
                                    node.links[k].linkZ = destZ = -1;
                                }
                            }
                            if (destY < 0 || destZ < 0)
                            {
                                continue;
                            }
                        }
                    }
                    if (node.input != 0)
                    {
                        writeNerualData(tid,currIdx++, 0, j, z);
                        hascomp = true;
                    }
                }
            }
            if (hascomp)
            {
                SoundNeuralNetToShaderFS.Compute c = SoundNeuralNetToShaderFS.EnqueueCompute(this, neuralData[tid], currIdx);
                while (c.DataGet == null)
                {
                    Thread.Sleep(sleep);
                }
                neuralData[tid] = c.DataGet;

                readNeuralData(tid,currIdx);
                hascomp = false;
                currIdx = 0;
            }
        }
        //Debug.Log("ThreadUpdate:" + ti);
        
        // hide layer  0111/111/111/111
        for (short i = (short)(1 + (n * tid)); i <= ((tid + ((tid + 1 == threadNum) ? 2 : 1)) * n) && i <= hideLayNum; i++)
        {
            //Debug.Log("ThreadUpdate:"+ti+"-" + i);
            for (short j = 0; j < hideWidth; j++)
            {
                for (int z = 0; z < hideHeight; z++)
                {
                    NeuralNode node = GetNode(i, j, z);

                    //Debug.Log("NNUpdateHideLay: "+i+","+j+": "+v);
                    for (k = 0; k < hideLinNum; k++)
                    {
                        destX = node.links[k].linkX;
                        destY = node.links[k].linkY;
                        destZ = node.links[k].linkZ;
                        if (destX < 0 || destY < 0 || destZ < 0)
                        {
                            if (i > 0 && i < hideLayNum)
                            {
                                if (seatchLink)
                                {
                                    //雷达式搜索可连接节点
                                    for (short l = 1; l <= hideLinLen && destX == -1; l++)
                                    {
                                        // 圈右
                                        if (i + l <= hideLayNum)
                                        {
                                            for (short m = (short)-l; m < l && destX == -1; m++)
                                            {
                                                short x = (short)(i + l), y = (short)(j + m);
                                                if (y < 0) continue;
                                                if (y >= hideWidth) break;
                                                for (int sz = Math.Max(0, z - l); sz <= Math.Min(hideHeight - 1, z + l); sz++)
                                                {
                                                    if (getInputLayer(x, y, sz) > 0)
                                                    {
                                                        node.links[k].linkX = destX = x;
                                                        node.links[k].linkY = destY = y;
                                                        node.links[k].linkZ = destZ = (short)sz;
                                                        node.links[k].power = random.Next(-9999, 10000) / 10000f;
                                                        //Debug.Log("NNUpdateHideLay: search link ok! "+i+","+j+"=>"+l+","+m);
                                                        break;
                                                    }
                                                }
                                            }
                                        }
                                        //圈底
                                        if (j + l < hideWidth)
                                        {
                                            for (short m = l; m > -l && destX == -1; m--)
                                            {
                                                short x = (short)(i + m), y = (short)(j + l);
                                                if (x <= 0) continue;
                                                if (x > hideLayNum || y >= hideWidth) break;
                                                for (int sz = Math.Max(0, z - l); sz <= Math.Min(hideHeight - 1, z + l); sz++)
                                                {
                                                    if (getInputLayer(x, y, sz) > 0)
                                                    {
                                                        node.links[k].linkX = destX = x;
                                                        node.links[k].linkY = destY = y;
                                                        node.links[k].linkZ = destZ = (short)sz;
                                                        node.links[k].power = random.Next(-9999, 10000) / 10000f;
                                                        //Debug.Log("NNUpdateHideLay: search link ok! "+i+","+j+"=>"+l+","+m);
                                                        break;
                                                    }
                                                }
                                            }
                                        }
                                        //圈左
                                        if (i - l > 0)
                                        {
                                            for (short m = l; m > -l && destX == -1; m--)
                                            {
                                                short x = (short)(i - l), y = (short)(j + m);
                                                if (y < 0) continue;
                                                if (y >= hideWidth) break;
                                                for (int sz = Math.Max(0, z - l); sz <= Math.Min(hideHeight - 1, z + l); sz++)
                                                {
                                                    if (getInputLayer(x, y, sz) > 0)
                                                    {
                                                        node.links[k].linkX = destX = x;
                                                        node.links[k].linkY = destY = y;
                                                        node.links[k].linkZ = destZ = (short)sz;
                                                        node.links[k].power = random.Next(-9999, 10000) / 10000f;
                                                        //Debug.Log("NNUpdateHideLay: search link ok! "+i+","+j+"=>"+l+","+m);
                                                        break;
                                                    }
                                                }
                                            }
                                        }
                                        // 圈顶
                                        if (j - l >= 0)
                                        {
                                            for (short m = (short)-l; m < l && destX == -1; m++)
                                            {
                                                short x = (short)(i + m), y = (short)(j - l);
                                                if (x <= 0) continue;
                                                if (x > hideLayNum || y >= hideWidth) break;
                                                for (int sz = Math.Max(0, z - l); sz <= Math.Min(hideHeight - 1, z + l); sz++)
                                                {
                                                    if (getInputLayer(x, y, sz) > 0)
                                                    {
                                                        node.links[k].linkX = destX = x;
                                                        node.links[k].linkY = destY = y;
                                                        node.links[k].linkZ = destZ = (short)sz;
                                                        node.links[k].power = random.Next(-9999, 10000) / 10000f;
                                                        //Debug.Log("NNUpdateHideLay: search link ok! "+i+","+j+"=>"+l+","+m);
                                                        break;
                                                    }
                                                }
                                            }
                                        }
                                        //顶层
                                        if (z + l < hideHeight)
                                        {
                                            for (short m = (short)(-l + 1); m < l && destX == -1; m++)
                                            {
                                                short x = (short)(i + m), sz = (short)(z + l);
                                                if (x <= 0) continue;
                                                if (x > hideLayNum) break;
                                                for (short y = (short)Math.Max(0, j - l + 1); y <= Math.Min(hideWidth - 1, j + l - 1); y++)
                                                {
                                                    if (getInputLayer(x, y, sz) > 0)
                                                    {
                                                        node.links[k].linkX = destX = x;
                                                        node.links[k].linkY = destY = y;
                                                        node.links[k].linkZ = destZ = (short)sz;
                                                        node.links[k].power = random.Next(-9999, 10000) / 10000f;
                                                        //Debug.Log("NNUpdateHideLay: search link ok! "+i+","+j+"=>"+l+","+m);
                                                        break;
                                                    }
                                                }
                                            }
                                        }
                                        //底层
                                        if (z - l >= 0)
                                        {
                                            for (short m = (short)(-l + 1); m < l && destX == -1; m++)
                                            {
                                                short x = (short)(i + m), sz = (short)(z - l);
                                                if (x <= 0) continue;
                                                if (x > hideLayNum) break;
                                                for (short y = (short)Math.Max(0, j - l + 1); y <= Math.Min(hideWidth - 1, j + l - 1); y++)
                                                {
                                                    if (getInputLayer(x, y, sz) > 0)
                                                    {
                                                        node.links[k].linkX = destX = x;
                                                        node.links[k].linkY = destY = y;
                                                        node.links[k].linkZ = destZ = (short)sz;
                                                        node.links[k].power = random.Next(-9999, 10000) / 10000f;
                                                        //Debug.Log("NNUpdateHideLay: search link ok! "+i+","+j+"=>"+l+","+m);
                                                        break;
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    node.links[k].linkX = destX = (short)random.Next(Mathf.Max(1, i - hideLinLen), Mathf.Min(i + hideLinLen + 1, hideLayNum + 1));
                                    node.links[k].linkY = destY = (short)random.Next(Mathf.Max(0, j - hideLinLen), Mathf.Min(j + hideLinLen + 1, hideWidth));
                                    node.links[k].linkZ = destZ = (short)random.Next(Math.Max(0, z - hideLinLen), Math.Min(z + hideLinLen + 1, hideHeight));
                                    if (getInputLayer(destX, destY, destZ) > 0)
                                    {
                                        node.links[k].power = random.Next(-9999, 10000) / 10000f;// * (layer[i][j][z] > layer[offsetX][offsetY][offsetZ] ? 1f : -1f);
                                    }
                                    else
                                    {
                                        node.links[k].linkX = destX = -1;
                                    }
                                }
                            }
                            else
                            {
                                int sj = (int)Math.Round((1f * j / hideWidth) * outputNum);
                                int sz = (int)Math.Round((1f * z / hideHeight) * outputHeight);
                                if (seatchLink)
                                {
                                    for (int m = Math.Max(0, sj - hideLinLen); destX < 0 && m < Math.Min(sj + hideLinLen + 1, outputNum); m++)
                                    {
                                        short x = (short)(hideLayNum + 1), y = (short)m;
                                        for (int h = Math.Max(0, sz - hideLinLen); h < Math.Min(sz + hideLinLen + 1, outputHeight); h++)
                                        {
                                            if (getInputLayer(x, y, h) > 0)
                                            {
                                                node.links[k].linkX = destX = x;
                                                node.links[k].linkY = destY = y;
                                                node.links[k].linkZ = destZ = (short)h;
                                                node.links[k].power = random.Next(-9999, 10000) / 10000f;// * (layer[i][j][z] > layer[offsetX][offsetY][sz] ? 1f : -1f);
                                                break;

                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    node.links[k].linkX = destX = (short)(hideLayNum + 1);
                                    node.links[k].linkY = destY = (short)random.Next(Math.Max(0, sj - hideLinLen), Math.Min(sj + hideLinLen + 1, outputNum));
                                    node.links[k].linkZ = destZ = (short)random.Next(Math.Max(0, sz - hideLinLen), Math.Min(sz + hideLinLen + 1, outputHeight));
                                    if (getInputLayer(destX, destY, destZ) > 0)
                                    {
                                        node.links[k].power = random.Next(-9999, 10000) / 10000f;// * (layer[i][j][z] > layer[offsetX][offsetY][sz] ? 1f : -1f);
                                    }
                                    else
                                    {
                                        node.links[k].linkX = destX = -1;
                                    }

                                }
                            }
                            if (destX < 0 || destY < 0 || destZ < 0)
                            {
                                continue;//搜索完后处理下一个链接
                            }
                        }
                    }

                    if (node.input != 0)
                    {
                        writeNerualData(tid, currIdx++, i, j, z);
                        hascomp = true;
                    }
                    if (!runing)
                        return;
                }
            }
            if (hascomp)
            {
                SoundNeuralNetToShaderFS.Compute c = SoundNeuralNetToShaderFS.EnqueueCompute(this, neuralData[tid],currIdx);
                while (c.DataGet == null)
                {
                    Thread.Sleep(sleep);
                }
                neuralData[tid] = c.DataGet;
                readNeuralData(tid,currIdx);
                hascomp = false;
                currIdx = 0;
            }
            else
            {
                //readNeuralData(tid,currIdx);
            }

        }

        // output layer
        if (tid == threadNum - 1)
        {
            try
            {
                run();
                for (int j = 0; j < outputNum; j++)
                {
                    for (int z = 0; z < outputHeight; z++)
                    {
                        if (GetNode(hideLayNum + 1,j,z).input != 0)
                        {
                            writeNerualData(tid, currIdx++, hideLayNum + 1, j, z);
                            hascomp = true;
                        }
                    }
                }
                if (hascomp)
                {
                    SoundNeuralNetToShaderFS.Compute c = SoundNeuralNetToShaderFS.EnqueueCompute(this, neuralData[tid],currIdx);
                    while (c.DataGet == null)
                    {
                        Thread.Sleep(sleep);
                    }
                    neuralData[tid] = c.DataGet;
                    readNeuralData(tid,currIdx);
                    hascomp = false;
                    currIdx = 0;
                }
            }
            catch (Exception ex)
            {
                Debug.LogError(ex.ToString());
            }
        }
    }

    private void writeNerualData(int tid,int currIdx, int i, int j, int z)
    {

        NeuralData[] neuralDatas = neuralData[tid];
        NeuralData data = neuralDatas[currIdx]; ;
        //if (i == 0)
        //{
        //    data = neuralDatas[j * inputHeight + z];
        //}
        //else if (i <= hideLayNum)
        //{
        //    data = neuralDatas[j * hideHeight + z];
        //}
        //else
        //{
        //    data = neuralDatas[j * outputHeight + z];
        //}
        data.x = i;
        data.y = j;
        data.z = z;
        NeuralNode node = GetNode(i,j,z);
        data.input = node.input;
        data.layer = node.layer;

        //if (data.input!=0)
        //{
        //    Debug.Log(name + " writeNerualData  " + data.x+","+data.y+","+data.z+"  "+data.input+"/"+data.layer);
        //}

        if (i <= hideLayNum)
        {
            //data.link1 = new NeuralLink();
            int lx = node.links[0].linkX;
            int ly = node.links[0].linkY;
            int lz = node.links[0].linkZ;
            if (lx >= 0 && ly >= 0 && lz >= 0)
            {
                data.link1.input = 0;
                data.link1.power = node.links[0].power;
                data.link1.layer = GetNode(lx,ly,lz).layer;
            }
            else
            {
                data.link1.input = 0;
                data.link1.layer = 0;
                data.link1.power = 0;
            }

            //data.link2 = new NeuralLink();
            int lx2 = node.links[1].linkX;
            int ly2 = node.links[1].linkY;
            int lz2 = node.links[1].linkZ;
            if (lx2 >= 0 && ly2 >= 0 && lz2 >= 0)
            {
                data.link2.input = 0;
                data.link2.power = node.links[1].power;
                data.link2.layer = GetNode(lx2,ly2,lz2).layer;
            }
            else
            {
                data.link2.input = 0;
                data.link2.layer = 0;
                data.link2.power = 0;
            }

            //data.link3 = new NeuralLink();
            int lx3 = node.links[2].linkX;
            int ly3 = node.links[2].linkY;
            int lz3 = node.links[2].linkZ;
            if (lx3 >= 0 && ly3 >= 0 && lz3 >= 0)
            {
                data.link3.input = 0;
                data.link3.power = node.links[2].power;
                data.link3.layer = GetNode(lx3,ly3,lz3).layer;
            }
            else
            {
                data.link3.input = 0;
                data.link3.layer = 0;
                data.link3.power = 0;
            }
        }
        neuralDatas[currIdx] = data;
        //if (i == 0)
        //{
        //    neuralDatas[j * inputHeight + z] = data;
        //}
        //else if (i <= hideLayNum)
        //{
        //    neuralDatas[j * hideHeight + z] = data;
        //}
        //else
        //{
        //    neuralDatas[j * outputHeight + z] = data;
        //}
    }

    private void readNeuralData(int tid, int count)
    {
        for (int i = 0; i < count; i++)
        {
            NeuralData data = neuralData[tid][i];
            NeuralNode node = GetNode(data.x,data.y,data.z);
            node.input = data.input;
            node.layer = data.layer;

            //if (data.x>= hideLayNum && data.layer != 0)
            //{
            //    Debug.Log(name+" neural " + data.x + "," + data.y + ", " + data.z + "  " + data.input + "/" + data.layer);
            //}
            if (data.x <= hideLayNum)
            {
                int lX = node.links[0].linkX;
                int lY = node.links[0].linkY;
                int lZ = node.links[0].linkZ;
                if (lX >= 0 && lY >= 0 && lZ >= 0)
                {
                    GetNode(lX,lY,lZ).input += (short)data.link1.input;
                    node.links[0].power = data.link1.power;
                    //if (i % 1000 == 0&& data.link1.input!= 0)
                    //{
                    //    Debug.Log(data.x + "," + data.y + "," + data.z + " -0- (" + linkX + "," + linkY + "," + linkZ + ")  " + data.link1.input + "/"+ data.link1.layer + "/" + data.link1.power);
                    //}
                    if (data.link1.power == 0) { node.links[0].linkX = -1; }
                }

                int lX2 = node.links[1].linkX;
                int lY2 = node.links[1].linkY;
                int lZ2 = node.links[1].linkZ;
                if (lX2 >= 0 && lY2 >= 0 && lZ2 >= 0)
                {
                    GetNode(lX2, lY2, lZ2).input += (short)data.link2.input;
                    node.links[1].power = data.link2.power;
                    //if (i % 1000 == 0 && data.link2.input != 0)
                    //{
                    //    Debug.Log(data.x + "," + data.y + "," + data.z + " -1- (" + linkX2 + "," + linkY2 + "," + linkZ2 + ")  " + data.link2.input + "/" + data.link2.layer + "/" + data.link2.power);
                    //}
                    if (data.link2.power == 0) { node.links[1].linkX = -1; }
                }

                int lX3 = node.links[2].linkX;
                int lY3 = node.links[2].linkY;
                int lZ3 = node.links[2].linkZ;
                if (lX3 >= 0 && lY3 >= 0 && lZ3 >= 0)
                {
                    GetNode(lX3, lY3, lZ3).input += (short)data.link3.input;
                    node.links[2].power = data.link3.power;
                    //if (i % 1000 == 0 && data.link3.input != 0)
                    //{
                    //    Debug.Log(data.x + "," + data.y + "," + data.z + " -2- (" + linkX3 + "," + linkY3 + "," + linkZ3 + ")  " + data.link3.input + "/" + data.link3.layer + "/" + data.link3.power);
                    //}
                    if (data.link3.power == 0) { node.links[2].linkX = -1; }
                }
            }
            //SaveNode(node);
        }
    }

    // Update is called once per frame
    public virtual void ThreadUpdate()
    {
        //Debug.Log("RootThreadUpdate");

        //if (float.IsInfinity(input[hideLayNum + 1][i]))
        //{
        //    input[hideLayNum + 1][i] = 0;
        //}
        //if (float.IsNegativeInfinity(layer[hideLayNum + 1][i]))
        //{
        //    layer[hideLayNum + 1][i] = 0;
        //}
        //if (float.IsNaN(layer[hideLayNum + 1][i]))
        //{
        //    layer[hideLayNum + 1][i] = 0;
        //}


    }

    public void OnDestroy()
    {
        runing = false;
    }

    public void OnApplicationQuit() {
       
        Debug.Log("NN: OnApplicationQuit");
    }

    internal void OnSerialize(BinaryWriter bfs, NeuralNode node)
    {
        bfs.Write(node.input);
        bfs.Write(node.layer);

        for (int k = 0; k < node.links.Length; k++)
        {
            bfs.Write(node.links[k].linkX);
            bfs.Write(node.links[k].linkY);
            bfs.Write(node.links[k].linkZ);
            bfs.Write(node.links[k].power);
        }

        bfs.Flush();
        bfs.Close();
    }

    internal void OnSerialize(BinaryWriter bfs)
    {
        bfs.Write((short)inputNum);
        bfs.Write((short)inputHeight);
        bfs.Write((short)outputNum);
        bfs.Write((short)outputHeight);
        bfs.Write((short)hideLayNum);
        bfs.Write((short)hideWidth);
        bfs.Write((short)hideHeight);
        bfs.Write((short)hideLinLen);
        bfs.Write((short)hideLinNum);
        bfs.Write((short)threadNum);
        bfs.Write(inputDecRate);
        bfs.Write(powerIncRate);
        bfs.Write(powerDecRate);
        bfs.Write(layerIncRate); 
        bfs.Write(layerDecRate);
        bfs.Write((short)sleep);
        bfs.Write((short)updateSpeed);

        bfs.Flush();
        bfs.Close();
    }

    internal void OnUnSerialize(BinaryReader bfs, NeuralNode node)
    {
        node.input = bfs.ReadSingle();
        node.layer = bfs.ReadSingle();

        for (int k = 0; k < node.links.Length; k++)
        {
            node.links[k].linkX = bfs.ReadInt16();
            node.links[k].linkY = bfs.ReadInt16();
            node.links[k].linkZ = bfs.ReadInt16();
            node.links[k].power = bfs.ReadSingle();
        }
        bfs.Close();
    }

    internal void OnUnSerialize(BinaryReader bfs)
    {
        inputNum = bfs.ReadInt16();
        inputHeight = bfs.ReadInt16();
        outputNum = bfs.ReadInt16();
        outputHeight = bfs.ReadInt16();
        hideLayNum = bfs.ReadInt16();
        hideWidth = bfs.ReadInt16();
        hideHeight = bfs.ReadInt16();
        hideLinLen = bfs.ReadInt16();
        hideLinNum = bfs.ReadInt16();
        threadNum = bfs.ReadInt16();
        inputDecRate = bfs.ReadSingle();
        powerIncRate = bfs.ReadSingle();
        powerDecRate = bfs.ReadSingle();
        layerIncRate = bfs.ReadSingle();
        layerDecRate = bfs.ReadSingle();
        sleep = bfs.ReadInt16();
        updateSpeed = bfs.ReadInt16();

        bfs.Close();
    }

    internal void StartLoad()
    {
        persistentDataPath = Application.persistentDataPath;

        loadData++;

        Thread rthread = new Thread(() =>
        {
            Debug.Log("LoadDataStart");

            LoadData();

            while (loadData != 0)
            {
                Thread.Sleep(1000);
            }
            Start();

            Debug.Log("LoadDataEnd");


            // 定时存档
            while (runing)
            {
                Thread.Sleep(saveTime * 60000);
                Debug.Log("SaveDataStart");
                SaveData();
                Debug.Log("SaveDataEnd");
            }
        });
        rthread.IsBackground = true;
        rthread.Start();
    }

    private bool LoadData()
    {
        bool suc = false;
        if (suc=BinaryDeserialize(name + ".dat"))
        {
            //if (layer == null)
            //{
            //    Debug.Log("Load " + name + " ReInit");
            //    Init();
            //}
            //if (nn.inputNum != inputNum || nn.outputNum != outputNum || nn.hideLayNum != hideLayNum || nn.hideWidth != hideWidth || nn.hideLinNum != hideLinNum)
            //{
            //    neuralNet = nn;
            //    Debug.Log("Load " + name + " Data version error");
            //    //Init();
            //}
            //else
            //{
            //    Debug.Log("Load " + name + " success");
            //    neuralNet = nn;
            //}
        }
        else
        {
            Init();
        }


        InitInput();

        lock (this)
        {
            loadData--;
        }
        return suc;
    }
    public void SaveData()
    {
        if (saving) return;

        saving = true;

        bool suc = BinarySerialize(name + ".dat");
        Debug.Log("Save " + name + ".dat " + suc);
        int i = 0, j = 0;
        foreach (List<Hashtable> item in tables)
        {
            foreach (Hashtable nodes in item)
            {
                List<object> list = new List<object>();
                foreach(var obj in nodes.Keys)
                {
                    list.Add(obj);
                }
                foreach (object z in list)
                {
                    NeuralNode node = (NeuralNode)nodes[z];
                    if (node != null)
                    {
                        if (SaveNode(node))
                        {
                            lock (nodes)
                            {
                                nodes.Remove(z);
                            }
                            //Debug.Log("Save " + name + " node " + node.x + "," + node.y + "," + node.z + "  " + node.input + "/" + node.layer+"  success");
                        }
                        else
                        {
                            Debug.LogError("Save " + name + " node " + node.x + "," + node.y + "," + node.z + "  " + node.input + "/" + node.layer+"  fault");
                        }

                    }
                    else
                    {
                        Debug.LogWarning("Save " + name + " node " +i+","+j+","+ z+"  not cache!");
                    }
                    if (runing == false)
                    {
                        return;
                    }
                }
                j++;
            }
            i++;
        }
                
        saving = false;
    }

    /// <summary>
    /// 序列化二进制
    /// </summary>
    public bool BinarySerialize(string filePath)
    {
        //if (!File.Exists(outputPath))
        //{
        //    Debug.LogError($"序列化二进制失败，输出路径有误，filePath：{outputPath}");
        //    return false;
        //}
        try
        {
            String tmpPath = persistentDataPath + "/" + filePath+".tmp";
            
            Debug.Log("Save path: " + tmpPath);

            using (FileStream fs = new FileStream(tmpPath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
            {
                //BinaryFormatter binaryFormatter = new BinaryFormatter();
                //binaryFormatter.Serialize(fs, obj);
                BufferedStream bfs = new BufferedStream(fs);
                OnSerialize(new BinaryWriter(bfs));
                bfs.Close();
                fs.Close();
            }
            
            File.Delete(persistentDataPath + "/" + filePath);
            File.Move(tmpPath, persistentDataPath + "/" + filePath);
            return true;
        }
        catch (Exception e)
        {
            Debug.LogError("序列化二进制失败，" + e);
           
        }
        return false;
    }

    /// <summary>
    /// 序列化二进制
    /// </summary>
    public bool BinarySerialize(NeuralNode node)
    {
        //if (!File.Exists(outputPath))
        //{
        //    Debug.LogError($"序列化二进制失败，输出路径有误，filePath：{outputPath}");
        //    return false;
        //}
        try
        {
            String tmpPath = persistentDataPath + "/" + name;
            string dirPath = Path.GetDirectoryName(tmpPath);
            if (!Directory.Exists(dirPath))
            {
                Directory.CreateDirectory(dirPath);
            }
            dirPath = Path.GetDirectoryName(tmpPath+"/"+node.x+"/");
            if (!Directory.Exists(dirPath))
            {
                Directory.CreateDirectory(dirPath);
            }
            dirPath = Path.GetDirectoryName(tmpPath+"/" + node.x + "/" + node.y+"/");
            if (!Directory.Exists(dirPath))
            {
                Directory.CreateDirectory(dirPath);
            }
            tmpPath = dirPath + "/"+node.z+".dat.tmp";
            string filePath = dirPath + "/" + node.z + ".dat";

            //Debug.Log("Save Node path: " + filePath);
            
            using (FileStream fs = new FileStream(tmpPath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
            {
                //BinaryFormatter binaryFormatter = new BinaryFormatter();
                //binaryFormatter.Serialize(fs, obj);
                BufferedStream bfs = new BufferedStream(fs);
                OnSerialize(new BinaryWriter(bfs), node);
                bfs.Close();
                fs.Close();
            }
            
            File.Delete(filePath);
            File.Move(tmpPath, filePath);
            return true;
        }
        catch (Exception e)
        {
            Debug.LogError("序列化二进制失败，" + e);
            
        }
        return false;
    }

    /// <summary>
    /// 反序列化二进制
    /// </summary>
    public bool BinaryDeserialize(string filename)
    {
        string binaryFilePath = persistentDataPath + "/" +filename;
        Debug.Log("Load Path: " + binaryFilePath);
        if (!File.Exists(binaryFilePath))
        {
            Debug.LogWarning("反序列化失败，找不到文件：" + binaryFilePath);
            return false;
        }
        try
        {
            using (FileStream fs = File.OpenRead(binaryFilePath))
            {
                //BinaryFormatter binaryFormatter = new BinaryFormatter();
                //obj = (T)binaryFormatter.Deserialize(fs);
                BufferedStream bfs = new BufferedStream(fs);
                OnUnSerialize(new BinaryReader(bfs));
                bfs.Close();
                fs.Close();
            }
            return true;
        }
        catch (Exception e)
        {
            Debug.LogError("反序列化失败，" + e);
            
        }
        return false;
    }

    /// <summary>
    /// 反序列化二进制
    /// </summary>
    public bool BinaryDeserialize(NeuralNode node)
    {
        string binaryFilePath = persistentDataPath + "/" + name + "/" + node.x + "/" + node.y + "/" + node.z + ".dat";
        //Debug.Log("Load Path: " + binaryFilePath);
        if (!File.Exists(binaryFilePath))
        {
            //Debug.LogWarning("反序列化失败，找不到文件：" + binaryFilePath);
            return false;
        }
        try
        {
            using (FileStream fs = File.OpenRead(binaryFilePath))
            {
                //BinaryFormatter binaryFormatter = new BinaryFormatter();
                //obj = (T)binaryFormatter.Deserialize(fs);
                BufferedStream bfs = new BufferedStream(fs);
                OnUnSerialize(new BinaryReader(bfs), node);
                bfs.Close();
                fs.Close();

                //Debug.Log("反序列化文件：" + binaryFilePath+"  success");

            }
            return true; 
        }
        catch (Exception e)
        {
            Debug.LogError("反序列化失败，" + e);
        }
        return false;
    }
}
